﻿using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using AIthin.Neuro;
using AIthin.Model.Body;
using AIthin.Model;


namespace AIthin.Controls
{
    /// <summary>
    /// Class NeuroControl to interact with NeuroNetwork
    /// </summary>
    public partial class NeuroControl : UserControl, INeuroControl
    {
        /// <summary>
        /// Unique network name for this network
        /// </summary>
        public String NetworkName = "NoName";
        /// <summary>
        /// interacted network
        /// </summary>
        public INetwork network = null;
        /// <summary>
        /// stream to learn
        /// </summary>
        LearnStream learnStream;
        /// <summary>
        /// Researchresult database
        /// </summary>
        RDBExpressDataSet DB;
        /// <summary>
        /// all input signals
        /// </summary>
        short[][,] learnInput = null;
        /// <summary>
        /// all output signalls
        /// </summary>
        short[][] learnOutput = null;

        short[,] inputFrame = null;

        /// <summary>
        /// current tick count
        /// </summary>
        int TickCount = 0;
        Bitmap bmRealitySnap;
        public String WorkingPath { get; set; }


        /// <summary>
        /// definition for the event 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void TickComputed(object sender, string e);
        /// <summary>
        /// send event when one tick will be computed
        /// </summary>
        public event TickComputed networkTickComputed;
        public NeuroControl()
        {
            InitializeComponent();
            learnStream = new LearnStream();

            Location = new System.Drawing.Point(12, 12);
            Name = "NC";
            Size = new System.Drawing.Size(613, 248);

            this.SetStyle(0
                              | ControlStyles.OptimizedDoubleBuffer //DoubleBuffer
                              | ControlStyles.UserPaint
                              | ControlStyles.AllPaintingInWmPaint
                              , true);

            this.UpdateStyles();

            labelLearnFile.Text = "";

            buttonStopLearn.Enabled = false;

            DB = new RDBExpressDataSet();

            bmRealitySnap = new Bitmap(2 * 64, 64, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

        }
        /// <summary>
        /// create control from file
        /// </summary>
        /// <param name="Path">Path to  control without slash</param>
        /// <param name="FileName">Filename with extention</param>
        public NeuroControl(String Path, String FileName)
            : this()
        {
            LoadNetworkFile(Path, FileName);
        }
        /// <summary>
        /// load network file *.nw1
        /// </summary>
        /// <param name="Path">file path without last slash \</param>
        /// <param name="FileName">file name with end suffix </param>
        public void LoadNetworkFile(String Path, String FileName)
        {
            String FullFileName = Path + "\\" + FileName;
            network = new Network();

            if (File.Exists(FullFileName))
            {
                network = network.Load(FullFileName);
                NetworkName = FileName.Remove(FileName.LastIndexOf('.'));

                labelName.Text = NetworkName;
            }
        }
        private bool loadLearnFile()
        {
            openLearnFileDialog.ShowDialog();
            String learnFileName = openLearnFileDialog.FileName;

            if (!learnStream.GetLearnSet(learnFileName, out learnInput, out learnOutput))
            {
                MessageBox.Show("File doesn´t exist");
                return false;
            }

            labelLearnFile.Text = "..." + learnFileName.Substring(learnFileName.Length - 52);
            this.Invalidate();
            return true;
        }
        private bool openLearnFile()
        {
            openLearnFileDialog.ShowDialog();
            String learnFileName = openLearnFileDialog.FileName;

            if (!learnStream.open(learnFileName))
            {
                MessageBox.Show("File doesn´t exist");
                return false;
            }

            labelLearnFile.Text = "..." + learnFileName.Substring(learnFileName.Length - 52);
            this.Invalidate();
            return true;
        }
        /// <summary>
        /// save network file *.nw1
        /// </summary>
        /// <param name="Path">file path without last slash \</param>
        /// <param name="FileName">file name with end suffix .nw</param>
        public void SaveNetworkFile(String Path)
        {
            network.Save(Path + "\\" + NetworkName + ".nw1");
            MessageBox.Show(" Network " + NetworkName + ".nw1" + " saved.");
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            if (learnStream != null)
            {
                g.DrawImage(learnStream.currentBitmap, 10, 10);
//                g.DrawImage(learnStream.inputBitmap, 10, 80);
//                g.DrawImage(learnStream.outputBitmap, 74, 80);
            }

             g.DrawImage(bmRealitySnap, 10, 80);
        }
        /// <summary>
        /// make a epoch or tick write data to control
        /// </summary>
        /// <param name="k">tick count</param>
        private void makeATick(int k)
        {
            if (k >= learnStream.LearnSets) return;

            // run learning iteration
            labelLearnCycles.Text = k.ToString();

            inputFrame = learnStream.getNextFrame();
            network.Tick(ref inputFrame);

            labelNeuronsCount.Text = network.NeuronsCount.ToString();

            if (networkTickComputed != null)  networkTickComputed(this, "tick computed");

            Invalidate();
            Application.DoEvents();
        }
        private void buttonStep_Click(object sender, EventArgs e)
        {
            if (TickCount < learnStream.LearnSets)
            {
                makeATick(TickCount);
                // next turn
                TickCount++;
            }

        }
        private void buttonStopLearn_Click(object sender, EventArgs e)
        {
            buttonStopLearn.Enabled = false;
        }
        private void learnToolStripMenuItem_Click(object sender, EventArgs e)
        {
            buttonStopLearn.Enabled = true;
            if (!openLearnFile())
            {
                MessageBox.Show("Cannt open learn file!");
                return;
            }
            //TODO does it work?
            Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
            // Thread.CurrentThread.Name = "Um";

            TickCount = 0;

            while (TickCount <  learnStream.LearnSets & buttonStopLearn.Enabled)
            {
                makeATick(TickCount++);
            }

        }
        private void workToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StructViewControl ViewControl = (StructViewControl)this.Parent.Controls["structViewControl2"];
            buttonStopLearn.Enabled = true;

            //   R = new Reality();
            Reality R = new Squash(64);

            // neue Body
            Wurm K = new Wurm(network);

            ViewControl.LoadNetwork(ref K.mind);

            Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
            // Thread.CurrentThread.Name = "Um";

            TickCount = 0;

            short[,] inp = new short[64, 64];

            while (TickCount++ < 300000 & buttonStopLearn.Enabled)
            {
                labelLearnCycles.Text = TickCount.ToString();
                labelNeuronsCount.Text = K.mind.NeuronsCount.ToString();

                // Realitätsbewegung pro einen Tick
                //draw matrix vor input
                R.Moving();

                // erstellt neues Schreiben in Bild
                Graphics g = Graphics.FromImage(bmRealitySnap);
                //R.Draw(g);
                R.Draw(ref inp);

                //Berechnen Reaktion auf das Bild durch Body (Wurm) pro Tick
                //Zwischenspeicher für vorherige Bilder soll beim Wurm sein
                //Eingangsdaten sind in Bild
                //Bewegung vom Wurm soll auch in Bild geschrieben werden.
  //              K.Reaction(bmRealitySnap);
                K.Reaction(ref inp);
                if (checkBoxDraw.Checked)
                {
                    if (networkTickComputed != null) networkTickComputed(this, "tick computed");
                }
                //wenn ball trifft wurm der bekommt beschädigung 
                if (R.GetBallPositionX() > K.muscle.Position &
                    R.GetBallPositionX() < K.muscle.Position + K.muscle.Length &
                    R.GetBallPositionY() == 8)
                {
                    //draw boom
                    g.FillRectangle(Brushes.LightCoral, R.GetBallPositionX(), 6, 16, 16);
                    labelMaxOutput.Text = K.damageCapacity.ToString();
                    labelMaxOutput.Refresh();

                    // wenn tod
                    if (K.IsDead())
                    {
                        //altes Speichern
                        K.mind.Save(WorkingPath + "\\wu32_" + K.mind.LifeTime.ToString() + ".nw1");
                        //neues erstellen
                        K = new Wurm("wu32");
                        //laden neues Network
                        ViewControl.LoadNetwork(ref K.mind);
                        ViewControl.Refresh();
                    }
                }
               
                Invalidate();
                //let another Apps to work
                Application.DoEvents();
            }

            //Save NN
            K.mind.Save(WorkingPath + "\\wu32_" + K.mind.LifeTime.ToString() + ".nw1");

        }
        private void viewToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }


    }
}
